package org.tim;

import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.tim.common.config.SslConfig;
import org.tim.common.config.TimConfig;
import org.tim.init.*;
import org.tim.listener.ServerAioListenerHandler;
import org.tim.protocol.ServerAioHandlerHandler;
import org.tim.util.ApplicationContextUtils;
import org.tio.server.ServerTioConfig;
import org.tio.server.TioServer;
import org.tio.utils.Threads;
import java.io.IOException;
import java.net.URI;


@Slf4j
@EnableConfigurationProperties({TimConfig.class, SslConfig.class})
public class CoreConfiguration {
    @Autowired
    private ConfigurableApplicationContext applicationContext;

    @Bean
    public ApplicationContextUtils applicationContextUtils() {
        return new ApplicationContextUtils();
    }

    /*---------------------------------------------------- start ----------------------------------------------------*/
    @Bean
    public ServerAioListenerHandler serverAioListenerHandler() {
        return new ServerAioListenerHandler();
    }

    @Bean
    public ServerAioHandlerHandler serverAioHandlerHandler() {
        return new ServerAioHandlerHandler();
    }

    @Bean
    public ServerTioConfig serverTioConfig(ServerAioListenerHandler serverAioListenerHandler, ServerAioHandlerHandler serverAioHandlerHandler) {
        ServerTioConfig serverTioConfig = new ServerTioConfig(this.getClass().getName(), serverAioHandlerHandler, serverAioListenerHandler, Threads.getTioExecutor(), Threads.getGroupExecutor());

        var list = Lists.newArrayList(applicationContext.getBeanFactory().getBeanProvider(TimInitializeHandler.class));
        AnnotationAwareOrderComparator.sort(list);
        var iterator = list.iterator();
        while (iterator.hasNext()) {
            var next = iterator.next();
            next.initialize(serverTioConfig);

            if(log.isDebugEnabled()) {
                log.debug("T-IM server " + next.getClass().getName() + "init success");
            }
        }
        return serverTioConfig;
    }

    @Bean
    public TioServer tioServer(ServerTioConfig serverTioConfig, TimConfig timConfig) {
        TioServer tioServer = new TioServer(serverTioConfig);

        var bind = URI.create(timConfig.getBind());
        try {
            tioServer.start(bind.getHost(), bind.getPort());

            if(log.isDebugEnabled()) {
                log.debug("T-IM server start success, host={}, port={}", bind.getHost(), bind.getPort());
            }
        } catch (IOException e) {
            if(log.isErrorEnabled()) {
                log.error("T-IM server start fail", e);
            }
            throw new RuntimeException();
        }
        return tioServer;
    }

    /*---------------------------------------------------- init ----------------------------------------------------*/
    @Bean
    public SslHandler sslHandler() {
        return new SslHandler();
    }

    @Bean
    public HealthCheckHandler healthCheckHandler() {
        return new HealthCheckHandler();
    }

    @Bean
    public ReadBufferSizeHandler readBufferSizeHandler() {
        return new ReadBufferSizeHandler();
    }

    @Bean
    public UseQueueDecodeHandler useQueueDecodeHandler() {
        return new UseQueueDecodeHandler();
    }

    @Bean
    public UseQueueSendHandler useQueueSendHandler() {
        return new UseQueueSendHandler();
    }
}
